import {
  remove,
  isDef,
  hasOwn,
  isArray,
  isFunction,
  invokeWithErrorHandling,
  warn
} from 'core/util'
import type { VNodeWithData } from 'types/vnode'
import { Component } from 'types/component'
import { isRef } from 'v3'
/**
 * 主要负责处理模板引用（refs）的创建、更新和销毁。
 * 在Vue中，ref是一个非常有用的特性，它允许你在模板中给元素或子组件注册引用信息
 * （即DOM元素或组件实例），并在Vue实例的$refs对象上进行访问。
 */

// 定义一个对象，包含create、update和destroy方法，这些方法会在组件的不同生命周期阶段被调用
export default {
  create(_: any, vnode: VNodeWithData) {
    // 在组件创建时注册ref
    registerRef(vnode)
  },
  update(oldVnode: VNodeWithData, vnode: VNodeWithData) {
    // 如果新旧vnode的ref不同，则先注销旧ref，再注册新ref
    if (oldVnode.data.ref !== vnode.data.ref) {
      registerRef(oldVnode, true)
      registerRef(vnode)
    }
  },
  destroy(vnode: VNodeWithData) {
    // 在组件销毁时注销ref
    registerRef(vnode, true)
  }
}
// registerRef函数负责实际的ref注册或注销逻辑
export function registerRef(vnode: VNodeWithData, isRemoval?: boolean) {
  const ref = vnode.data.ref // 获取vnode上的ref值
  if (!isDef(ref)) return // 如果没有定义ref，则直接返回
 
  const vm = vnode.context // 获取当前组件实例（Vue实例）
  const refValue = vnode.componentInstance || vnode.elm // 获取ref对应的值，如果是组件实例则取componentInstance，否则取DOM元素elm
  const value = isRemoval ? null : refValue // 如果是移除操作，则value为null，否则为refValue
  const $refsValue = isRemoval ? undefined : refValue // 如果是移除操作，则value为undefined，否则为refValue
 
  // 如果ref是一个函数，则调用该函数并传入值
  if (isFunction(ref)) {
    invokeWithErrorHandling(ref, vm, [value], vm, `template ref function`)
    return
  }

  const isFor = vnode.data.refInFor
  const _isString = typeof ref === 'string' || typeof ref === 'number'
  const _isRef = isRef(ref)
  const refs = vm.$refs

  if (_isString || _isRef) {
    if (isFor) {
      const existing = _isString ? refs[ref] : ref.value
      if (isRemoval) {
        isArray(existing) && remove(existing, refValue)
      } else {
        if (!isArray(existing)) {
          if (_isString) {
            refs[ref] = [refValue]
            setSetupRef(vm, ref, refs[ref])
          } else {
            ref.value = [refValue]
          }
        } else if (!existing.includes(refValue)) {
          existing.push(refValue)
        }
      }
    } else if (_isString) {
      if (isRemoval && refs[ref] !== refValue) {
        return
      }
      refs[ref] = $refsValue
      setSetupRef(vm, ref, value)
    } else if (_isRef) {
      if (isRemoval && ref.value !== refValue) {
        return
      }
      ref.value = value
      // 如果是开发模式，且ref类型无效，则发出警告
    } else if (__DEV__) {
      warn(`Invalid template ref type: ${typeof ref}`)
    }
  }
}
// setSetupRef函数用于在组件的setup状态中设置ref值
function setSetupRef(
  { _setupState }: Component,
  key: string | number,
  val: any
) {
  // 如果_setupState存在，并且key对应的值是一个ref对象，则更新其value
  // 否则，直接设置key对应的值
  if (_setupState && hasOwn(_setupState, key as string)) {
    if (isRef(_setupState[key])) {
      _setupState[key].value = val
    } else {
      _setupState[key] = val
    }
  }
}
